﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using System.Timers;
using System.Runtime.InteropServices;

namespace PovTeam.Pkg_PovrayProject
{
    class PovCodeWindowManager : IVsCodeWindowManager, Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget, IVsTextViewFilter
    {
        private IVsCodeWindow pCodeWin;
        private uint eventCookie;
        private PovIdentifierScanner scanner;

       

        public IVsCodeWindow CodeWindow
        {
            get
            {
                return pCodeWin;
            }
        }

        public Pkg_PovrayProjectPackage Package
        {
            get
            {
                return povLanguageService.Package;
            }
        }

        public bool ScanComplete
        {
            get
            {
                lock (scanLock)
                {
                    return _scanComplete;
                }
            }
        }

        public bool Scanning
        {
            get
            {
                lock (scanLock)
                {
                    return _scanning;
                }
            }
        }

        class ScanOptions
        {
            public bool includes;
            public bool scanFile;
            public string fileName;
        }
        
        private bool _scanning;
        Object scanLock = new Object();
        private void ScanningThread(object scanOptions)
        {
            lock (scanLock)
            {
                _scanComplete = false;
                _scanning = true;
            }

            ScanOptions options = scanOptions as ScanOptions;

            if (options.scanFile)
            {
                identifiers = identifiers.Union(scanner.ScanIncludes(options.fileName)).ToArray();
            }
            else
            {
                identifiers = scanner.GetIdentfierList(options.includes);
            }

            lock (scanLock)
            {
                _scanComplete = true;
                _scanning = false;
            }
        }

        private Identifier[] identifiers;

        public PovCodeWindowManager(IVsCodeWindow pCodeWin, PovLanguageService povLanguageService)
        {
            // TODO: Complete member initialization
            this.pCodeWin = pCodeWin;
            this.povLanguageService = povLanguageService;
            
            

            IVsTextView pView;
            pCodeWin.GetPrimaryView(out pView);
            pView.AddCommandFilter(this, out prevFilter);
            scanner = new PovIdentifierScanner(this);

            

            System.Threading.Thread thread = new System.Threading.Thread(ScanningThread);

            thread.Start(new ScanOptions() { includes = true, scanFile = false });

        }
        #region IVsCodeWindowManager Members

        public int AddAdornments()
        {
            return VSConstants.S_OK;
        }

        Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget prevFilter;

        public int OnNewView(IVsTextView pView)
        {


            return VSConstants.S_OK;
        }

        public int RemoveAdornments()
        {
            return VSConstants.S_OK;
        }

        #endregion



       

        private PovLanguageService povLanguageService;

        private Timer timer;


        

       

       

        

       

       

       
       




        #region IOleCommandTarget Members


        public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
        {

            if (pguidCmdGroup == VsMenus.guidStandardCommandSet2K)
            {
                if (nCmdID == (uint)VSConstants.VSStd2KCmdID.COMPLETEWORD)
                {
                    IVsTextView view;

                    CodeWindow.GetLastActiveView(out view);
                    IVsTextLines buffer;
                    view.GetBuffer(out buffer);

                   
                    int line, idx;
                    view.GetCaretPos(out line, out idx);
                    PovCodeContext context = new PovCodeContext(buffer);
                    IdentifierType[] typeList = context.ValidIdentifierTypes(line, idx);

                    TextSpan[] span = new TextSpan[1];
                    if (idx > 0) idx -= 1;
                    view.GetWordExtent(line, idx, 0, span);
                    Identifier[] completionList = identifiers.Where(i => typeList.Contains(i.Type)).ToArray();
                    if (completionList.Length > 0)
                    {
                        view.UpdateCompletionStatus(new PovCodeCompletion(completionList.Select(i => new CompletionItem(i.Name, i.Desc, i.Icon)).OrderBy(i => i.Text).ToArray(), span[0]), (uint)UpdateCompletionFlags.UCS_NAMESCHANGED);
                    }
                    return VSConstants.S_OK;
                }
                if (nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR)
                {
                    char ch = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn);

                    if (ch != ' ')
                    {
                        IVsTextView view;
                        pCodeWin.GetLastActiveView(out view);

                        IVsTextLines buffer;

                        view.GetBuffer(out buffer);

                        int line, idx;

                        view.GetCaretPos(out line, out idx);
                        PovCodeContext context = new PovCodeContext(buffer);
                        IdentifierType[] typeList = context.MinimalValidIdentifierTypes(line, idx);


                        TextSpan[] span = new TextSpan[1];
                        if (idx > 0) idx -= 1;
                        view.GetWordExtent(line, idx, 0, span);
                        Identifier[] completionList = identifiers.Where(i => typeList.Contains(i.Type)).ToArray();
                        if (completionList.Length > 0)
                        {
                            view.UpdateCompletionStatus(new PovCodeCompletion(completionList.Select(i => new CompletionItem(i.Name, i.Desc, i.Icon)).OrderBy(i => i.Text).ToArray(), span[0]), (uint)UpdateCompletionFlags.UCS_NAMESCHANGED);
                        }
                        else
                        {
                            view.UpdateCompletionStatus(null, 0);
                        }
                    }
                }

            }

            if (prevFilter != null)
            {
                return prevFilter.Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
            }
            return VSConstants.S_OK;
        }

        public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, Microsoft.VisualStudio.OLE.Interop.OLECMD[] prgCmds, IntPtr pCmdText)
        {
            if (pguidCmdGroup == VsMenus.guidStandardCommandSet2K)
            {

                if (prgCmds[0].cmdID == (uint)VSConstants.VSStd2KCmdID.COMPLETEWORD)
                {
                    prgCmds[0].cmdf = (uint)(Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_SUPPORTED | Microsoft.VisualStudio.OLE.Interop.OLECMDF.OLECMDF_ENABLED);
                    return VSConstants.S_OK;
                }
            }
            if (prevFilter != null)
            {
                return prevFilter.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);
            }
            else
            {
                return VSConstants.S_OK;
            }
        }

        #endregion

        #region IVsTextViewFilter Members

        public int GetDataTipText(TextSpan[] pSpan, out string pbstrText)
        {
            pbstrText = "";
            return VSConstants.S_OK;
        }

        public int GetPairExtents(int iLine, int iIndex, TextSpan[] pSpan)
        {
            throw new NotImplementedException();
        }

        public int GetWordExtent(int iLine, int iIndex, uint dwFlags, TextSpan[] pSpan)
        {
            WORDEXTFLAGS flags = (WORDEXTFLAGS)dwFlags;

            string sLine;
            IVsTextLines buffer;
            this.CodeWindow.GetBuffer(out buffer);
            int lineLen;
            buffer.GetLengthOfLine(iLine, out lineLen);

            if (iIndex >= lineLen)
            {
                pSpan[0].iStartLine = iLine;
                pSpan[0].iStartIndex = iIndex;
                pSpan[0].iEndIndex = iIndex;
                pSpan[0].iEndLine = iLine;
                return VSConstants.S_OK;
            }

            buffer.GetLineText(iLine, 0, iLine, lineLen, out sLine);

           // if (0 != (flags & WORDEXTFLAGS.WORDEXT_FINDWORD))
           // {
            //    if (0 != (flags & WORDEXTFLAGS.WORDEXT_CURRENT))
            //    {
                    int begin = iIndex; // move to the beginning 
                    int end = iIndex;

                    bool LastWasDigit = false;

                    while (begin > 0 && char.IsLetterOrDigit(sLine[begin]) || (LastWasDigit && (sLine[begin] == '.' || sLine[begin] == '-' )) || sLine[begin] == '_')
                    {
                        LastWasDigit = char.IsDigit(sLine[begin]);

                        begin--;
                    }

                    if (!char.IsLetter(sLine[begin]))
                    {
                        begin++;
                    }

                    while (end < sLine.Length && (char.IsLetterOrDigit(sLine[end]) || (LastWasDigit && sLine[end] == '.') || sLine[end] == '_'))
                    {
                        LastWasDigit = char.IsDigit(sLine[end]);
                        end++;
                    }

                    if (begin > end) begin = end;
                    if (end == begin) end += 1;

                    pSpan[0].iStartLine = iLine;
                    pSpan[0].iStartIndex = begin;
                    pSpan[0].iEndIndex = end;
                    pSpan[0].iEndLine = iLine;

                    return VSConstants.S_OK;

               // }
           // }

            return VSConstants.DISP_E_MEMBERNOTFOUND;
        }

        #endregion

        private bool _scanComplete;
    }
}
